import Dep, { pushTarget } from "./observe/dep";
import { observe } from "./observe/index";
import Watcher from "./observe/watcher";

export function initState(vm) {
    const options = vm.$options

    // 后续实现计算属性 、 watcher 、 props 、methods
    if (options.data) {
        initData(vm);
    }
    if (options.watch) {
        initWatch(vm);
    }
    if(options.computed){
        initComputed(vm);
    }
}

function proxy(vm, source, key) {
    Object.defineProperty(vm, key, {
        get() {
            return vm[source][key]
        },
        set(newValue) {
            vm[source][key] = newValue;
        }
    })
}

function initData(vm) {
    let data = vm.$options.data;
    // 如果是函数就拿到函数的返回值 否则就直接采用data作为数据源
    data = vm._data = typeof data === 'function' ? data.call(vm) : data

    // 属性劫持 采用defineProperty将所有的属性进行劫持

    // 我期望用户可以直接通过 vm.xxx 获取值， 也可以这样取值 vm._data.xxx
    for (let key in data) {
        proxy(vm, '_data', key)
    }
    observe(data)
}

function initWatch(vm) {
    const watch = vm.$options.watch;
    for (let key in watch) {
        let handler = watch[key];
        // 对当前属性进行创建watcher，watcher中存放的回调是handler，取数据是从vm上获取
        createWatcher(vm, key, handler);
    }
}

function createWatcher(vm, key, handler) {
    // handler可以为对象   
    let options;
    if (typeof handler === 'object' && handler !== null) {
        options = handler;
        handler = handler.handler
    }
    // 你可以判断如果handler 是一个字符串 可以采用实例上的方法
    if (typeof handler === 'string') {
        handler = vm[key];
    }
    return vm.$watch(key, handler, options)
}
export function stateMixin(Vue) {
    // watch的实现就是对属性创建watcher，属性变化后执行对应watcher中存放的回调
    Vue.prototype.$watch = function(key, handler, options = {}) {
        const vm = this;
        // options.immediate
        // vm,渲染函数，回调，用户的选项
        options.user = true
        const watcher = new Watcher(vm, key, handler, options); // watcher.value = 目前实例上的值

        if (options.immediate) {
            handler.call(vm, watcher.value); //去key 对象的值
        }
    }
}


function initComputed(vm){
    debugger;
    const computed = vm.$options.computed;
    const watchers = vm._computedWatchers = {}
    for(let key in computed){
        const userDef = computed[key];
        // getter是用户的getter
        const getter = typeof userDef === 'function' ? userDef : userDef.get;
        watchers[key] =  new Watcher(vm,getter,()=>{},{lazy:true});
        defineComputed(vm,key); // 用defineProperty 定义在我们的实例上
    }
}

function defineComputed(vm,key){
    // 每次取值都会执行
    Object.defineProperty(vm,key,{
        get:createComputedGetter(key)
    })
}
function createComputedGetter(key){
    // 如果dirty:true就重新计算，否则就不算了 把以前的值返回
    return function(){
        // 是否有需要重新计算，如果需要重新计算 就调用useDeffine
        const watcher =  this._computedWatchers[key];
        if(watcher.dirty){
            watcher.evaluate(); // 会调用用户定义的方法将返回值返回,此时dirty为false，并且用户的返回值存放到了watcher.value上
        }
       // 在求值的过程中 stack = [渲染watcher，计算属性watcher] Dep.target = 计算属性watcher
       // 当evaluate执行完毕后 stack = [渲染watcher]  Dep.target  = 渲染watcher

       if(Dep.target){ // 让计算属性watcher对应的两个dep 记录渲染watcher即可
            watcher.depend();
       }
       // 计算属性是一个watcher  渲染是一个watcher
        return watcher.value
    }
}
// pushTarget();
// function renderWatcher(){
//     pushTarget();
//     function computedWatcher(){

//     }
//     popTarget()
// }
// renderWatcher();
// popTarget()


// 周五，下次课将组件渲染。  周日 vuex , vue-router (vue3 + ts 组件库)
// 下周 面试题 ts 
// 大下周 vue3 + ts  写一个vue3+ts项目 （）